package EvalCalc;
use strict;
use Carp;

use Calculator::Operator;

####

sub f { 
  $_[0]>1?$_[0]*f($_[0]-1):1; 
}
 
sub fac {
  my $n = shift;

  confess "Not valid number" unless $n =~ /^\d+$/;
  f($n);
};

my $s = sub { shift() **  shift() };

Operator->make_sem(
   expr => {
      '+' => sub { shift()  +  shift() },
      '-' => sub { shift()  -  shift() },
   },
   term => {
     '*' => sub { shift()  *  shift() },
     '/' => sub { shift()  /  shift() },
   },
   power => {
      '^'  => $s, 
      '**' => $s,
   },
   uneg => { 
      'NEG' => sub { -shift() },
   },
   factorial => { 
      '!' => \&fac, 
   },
   statements => {
      ';' => sub { shift()  ;  shift(); }, #devuelve la ultima sentencia
   },
   exprequal => {
      '==' => sub { shift() ==  shift() },
      '!=' => sub { shift() !=  shift() },
   },
   exprrel => {
      '<' => sub { shift() <  shift() },
      '>' => sub { shift() >  shift() },
      '<=' => sub { shift() <=  shift() },
      '>=' => sub { shift() >=  shift() },
   },
);

no warnings 'redefine';

package lvalue;
sub ceval {
   my $self = shift;
   return $self->{var};
}


package rvalue;
sub ceval {
   my $self = shift;
   my $lvalue = $self->{var};
   my $rvalue = 0;
   if (defined($Calculator::symbol{$lvalue})){
      $rvalue = $Calculator::symbol{$lvalue};
   }
   else{
      print "variable no definida\n";
   }
   return $rvalue;
}

package factor;
sub ceval {
   my $self = shift;
   return $self->{val};
}


package assignation;
sub ceval{
   my $self = shift;
   my $expr = $self->{right}->ceval();
   my $var = $self->{left}->ceval();
   $Calculator::symbol{$var} = $expr;
   return $expr;
}

package ifstatement;

sub ceval{
   my $self = shift;
   if( $self->{expr}->ceval() ){
      return $self->{true}->ceval();
   }
   else{
      return $self->{false}->ceval() if defined $self->{false};
   }
   return '0'; #en el caso de que la expr sea false y no haya false
}


1;
